﻿using System.Timers;
using Devonline.Entity;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.Logging;

namespace Devonline.Communication.Client;

/// <summary>
/// 消息客户端
/// </summary>
public class MessageClient : CommunicationClient
{
    public MessageClient(ILogger<CommunicationClient> logger, ClientSetting setting) : base(logger, setting)
    {
    }

    /// <summary>
    /// 使用 message content 和 type 创建一个默认消息
    /// 默认的消息使用当前时间填充创建时间和发送时间
    /// </summary>
    /// <typeparam name="T">消息内容类型</typeparam>
    /// <param name="content">消息内容</param>
    /// <param name="type">消息类型</param>
    /// <returns></returns>
    public virtual Message<T> CreateMessage<T>(T content, MessageType type = MessageType.Text) => new()
    {
        Id = KeyGenerator.GetKey().ToString(),
        Sender = ClientId,
        Receiver = _clientSetting.Receiver,
        CreateTime = DateTime.UtcNow,
        Type = type,
        Content = content
    };
    /// <summary>
    /// 使用 message content, receiver 和 type 创建一个默认消息
    /// 默认的消息使用当前时间填充创建时间和发送时间
    /// </summary>
    /// <typeparam name="T">消息内容类型</typeparam>
    /// <param name="content">消息内容</param>
    /// <param name="receiver">消息接收者</param>
    /// <param name="type">消息类型</param>
    /// <returns></returns>
    public virtual Message<T> CreateMessage<T>(T content, string receiver, MessageType type = MessageType.Text) => new()
    {
        Id = KeyGenerator.GetKey().ToString(),
        Sender = ClientId,
        Receiver = receiver ?? _clientSetting.Receiver,
        CreateTime = DateTime.UtcNow,
        Type = type,
        Content = content
    };

    #region 客户端接收消息的方法
    /// <summary>
    /// 注册客户端接收消息的方法
    /// </summary>
    /// <typeparam name="T">消息内容</typeparam>
    /// <param name="action"></param>
    public virtual void OnReceive<T>(Action<Message<T>> action) => On("Receive", action);
    /// <summary>
    /// 注册客户端接收通知消息的方法
    /// </summary>
    /// <param name="action"></param>
    public virtual void OnNotice<T>(Action<Message<T>> action) => On(MessageType.Notice.ToString(), action);
    /// <summary>
    /// 注册客户端接收预警消息的方法
    /// </summary>
    /// <param name="action"></param>
    public virtual void OnAlarm<T>(Action<Message<T>> action) => On(MessageType.Alarm.ToString(), action);
    /// <summary>
    /// 注册客户端接收回复消息的方法
    /// </summary>
    /// <param name="action"></param>
    public virtual void OnReply<T>(Action<Message<T>> action) => On(MessageType.Reply.ToString(), action);
    /// <summary>
    /// 注册客户端接收回执消息的方法
    /// </summary>
    /// <param name="action"></param>
    public virtual void OnAck<T>(Action<string> action) => On(MessageType.Ack.ToString(), action);
    /// <summary>
    /// 注册客户端接收回执消息的方法
    /// </summary>
    /// <param name="action"></param>
    public virtual void OnRead<T>(Action<string> action) => On("Read", action);
    #endregion

    #region 客户端发送消息的方法
    /// <summary>
    /// 将文本消息内容发送到默认消息接收者
    /// </summary>
    /// <typeparam name="T">消息内容类型</typeparam>
    /// <param name="content">消息内容</param>
    /// <returns></returns>
    public override Task SendAsync<T>(T content) => InvokeAsync("SendMessage", CreateMessage(content, MessageType.Text));
    /// <summary>
    /// 将通知内容发送到默认消息接收者
    /// </summary>
    /// <typeparam name="T">消息内容类型</typeparam>
    /// <param name="content">消息内容</param>
    /// <returns></returns>
    public virtual Task NoticeAsync<T>(T content) => InvokeAsync(MessageType.Notice.ToString(), CreateMessage(content, MessageType.Notice));
    /// <summary>
    /// 将预警内容发送到到默认消息接收者
    /// </summary>
    /// <typeparam name="T">消息内容类型</typeparam>
    /// <param name="content">消息内容</param>
    /// <returns></returns>
    public virtual Task AlarmAsync<T>(T content) => InvokeAsync(MessageType.Alarm.ToString(), CreateMessage(content, MessageType.Alarm));
    /// <summary>
    /// 将回复内容发送到默认消息接收者
    /// </summary>
    /// <typeparam name="T">消息内容类型</typeparam>
    /// <param name="content">消息内容</param>
    /// <returns></returns>
    public virtual Task ReplyAsync<T>(T content) => InvokeAsync(MessageType.Reply.ToString(), CreateMessage(content, MessageType.Reply));

    /// <summary>
    /// 将文本消息内容发送到指定消息接收者
    /// </summary>
    /// <typeparam name="T">消息内容类型</typeparam>
    /// <param name="content">消息内容</param>
    /// <param name="receiver">消息接收者</param>
    /// <returns></returns>
    public override Task SendAsync<T>(T content, string receiver) => InvokeAsync("SendMessage", CreateMessage(content, receiver, MessageType.Text));
    /// <summary>
    /// 将通知内容发送到指定消息接收者
    /// </summary>
    /// <typeparam name="T">消息内容类型</typeparam>
    /// <param name="content">消息内容</param>
    /// <param name="receiver">消息接收者</param>
    /// <returns></returns>
    public virtual Task NoticeAsync<T>(T content, string receiver) => InvokeAsync(MessageType.Notice.ToString(), CreateMessage(content, receiver, MessageType.Notice));
    /// <summary>
    /// 将预警内容发送到到指定消息接收者
    /// </summary>
    /// <typeparam name="T">消息内容类型</typeparam>
    /// <param name="content">消息内容</param>
    /// <param name="receiver">消息接收者</param>
    /// <returns></returns>
    public virtual Task AlarmAsync<T>(T content, string receiver) => InvokeAsync(MessageType.Alarm.ToString(), CreateMessage(content, receiver, MessageType.Alarm));
    /// <summary>
    /// 将回复内容发送到指定消息接收者
    /// </summary>
    /// <typeparam name="T">消息内容类型</typeparam>
    /// <param name="content">消息内容</param>
    /// <param name="receiver">消息接收者</param>
    /// <returns></returns>
    public virtual Task ReplyAsync<T>(T content, string receiver) => InvokeAsync(MessageType.Reply.ToString(), CreateMessage(content, receiver, MessageType.Reply));

    /// <summary>
    /// 将完整消息发送到消息接收者, 消息类型需要自定义, 默认自动为文本类型
    /// </summary>
    /// <typeparam name="T">消息内容类型</typeparam>
    /// <param name="message">消息</param>
    /// <returns></returns>
    public virtual Task SendAsync<T>(Message<T> message)
    {
        ValidateMessage(message);
        InitialMessage(message);

        _logger.LogInformation("Communication Client {clientId} will Send the message {message} to {receiver}", ClientId, message.Content, message.Receiver);

        //发送前修改发送时间
        message.SendTime = DateTime.UtcNow;
        return _connection.InvokeAsync("SendMessage", message);
    }
    /// <summary>
    /// 将完整消息发送到消息接收者, 消息类型需要自定义, 默认自动为文本类型
    /// </summary>
    /// <typeparam name="T">消息内容类型</typeparam>
    /// <param name="message">消息</param>
    /// <returns></returns>
    public virtual Task SendAsync<T>(params Message<T>[] messages)
    {
        if (messages.Length > 0)
        {
            foreach (var message in messages)
            {
                ValidateMessage(message);
                InitialMessage(message);

                //发送前修改发送时间
                message.SendTime = DateTime.UtcNow;
            }

            _logger.LogInformation("Communication Client {clientId} will Send the messages {message} to receivers {receiver}", ClientId, messages.Select(x => x.Content?.ToString() ?? string.Empty).ToString<string>(), messages.Select(x => x.Receiver ?? string.Empty).Distinct().ToString<string>());
            return _connection.InvokeAsync("SendMessages", messages);
        }

        return Task.CompletedTask;
    }
    /// <summary>
    /// 将完整通知消息内容发送到指定消息接收者
    /// </summary>
    /// <typeparam name="T">消息内容类型</typeparam>
    /// <param name="message">消息</param>
    /// <returns></returns>
    public virtual Task NoticeAsync<T>(Message<T> message)
    {
        ValidateMessage(message);
        InitialMessage(message);
        message.Type = MessageType.Notice;
        return InvokeAsync(MessageType.Notice.ToString(), message);
    }
    /// <summary>
    /// 将完整预警消息内容发送到指定消息接收者
    /// </summary>
    /// <typeparam name="T">消息内容类型</typeparam>
    /// <param name="message">消息</param>
    /// <returns></returns>
    public virtual Task AlarmAsync<T>(Message<T> message)
    {
        ValidateMessage(message);
        InitialMessage(message);
        message.Type = MessageType.Alarm;
        return InvokeAsync(MessageType.Alarm.ToString(), message);
    }
    /// <summary>
    /// 将完整回复消息内容发送到指定消息接收者
    /// </summary>
    /// <typeparam name="T">消息内容类型</typeparam>
    /// <param name="message">消息</param>
    /// <returns></returns>
    public virtual Task ReplyAsync<T>(Message<T> message)
    {
        ValidateMessage(message);
        InitialMessage(message);
        message.Type = MessageType.Reply;
        return InvokeAsync(MessageType.Reply.ToString(), message);
    }

    /// <summary>
    /// 发送消息回执
    /// </summary>
    /// <param name="id">消息编号</param>
    /// <returns></returns>
    public virtual Task AckAsync(string id) => InvokeAsync(MessageType.Ack.ToString(), id);
    /// <summary>
    /// 发送已读消息
    /// </summary>
    /// <param name="id">消息编号</param>
    /// <returns></returns>
    public virtual Task ReadAsync(string id) => InvokeAsync("Read", id);
    /// <summary>
    /// 发送心跳
    /// </summary>
    /// <returns></returns>
    public virtual Task HeartbeatAsync() => InvokeAsync(MessageType.Heartbeat.ToString());
    #endregion

    #region 集成或重写的方法, 内部方法
    protected virtual void On<T>(string methodName, Action<Message<T>> action)
    {
        _connection.Remove(methodName);
        _connection.On<Message<T>>(methodName, message =>
        {
            try
            {
                _logger.LogInformation("Communication Client {clientId} receive the the {messageType} message {message} from {sender}", ClientId, message.Type, message.Content, message.Sender);
                message.ReceiveTime = DateTime.Now;
                if (message.Type < MessageType.Ack && message.Sender != ClientId)
                {
                    Task.Run(async () => await AckAsync(message.Id));
                }

                action?.Invoke(message);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Communication Client {clientId} receive the message {message} from {sender} throw exception", ClientId, message.ToJsonString(), message.Sender);
            }
        });
    }
    /// <summary>
    /// 默认的调用服务器的执行方法
    /// </summary>
    /// <param name="methodName"></param>
    /// <param name="message"></param>
    /// <returns></returns>
    protected virtual Task<T> InvokeAsync<T>(string methodName, Message<T> message)
    {
        _logger.LogInformation("Communication Client {clientId} will {method} the message {message} to {receiver}", ClientId, methodName, message.Content, message.Receiver);

        //发送前修改发送时间
        message.SendTime = DateTime.UtcNow;
        return _connection.InvokeAsync<T>(methodName, message);
    }
    /// <summary>
    /// 客户端连接监控
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    protected override async void MonitorAsync(object? sender, ElapsedEventArgs args)
    {
        if (_isRunning)
        {
            try
            {
                if (_connection.State == HubConnectionState.Connected)
                {
                    _logger.LogDebug("Communication Client {clientId} send heartbeat to the host {host}", ClientId, Host);
                    await HeartbeatAsync();
                }
                else
                {
                    _logger.LogWarning("Communication Client {clientId} disconnected the {host}, now will reconnect", ClientId, Host);
                    await ConnectAsync();
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Communication Client {clientId} monitor throw exception", ClientId);
            }
        }
    }
    /// <summary>
    /// 验证消息体
    /// </summary>
    /// <param name="message"></param>
    /// <exception cref="Exception"></exception>
    protected void ValidateMessage<T>(Message<T> message)
    {
        if (message == null)
        {
            throw new ArgumentNullException(nameof(message), "The message can't be null");
        }

        if (message.Content == null)
        {
            throw new ArgumentNullException(nameof(message.Content), "The message content can't be null");
        }

        if (string.IsNullOrWhiteSpace(message.Receiver) && string.IsNullOrWhiteSpace(message.Group))
        {
            throw new ArgumentNullException(nameof(message), "The message has no receiver or receive group");
        }
    }
    /// <summary>
    /// 通用消息处理
    /// </summary>
    /// <param name="message"></param>
    /// <returns></returns>
    protected void InitialMessage<T>(Message<T> message)
    {
        //填充消息编号和消息当前连接
        message.Id ??= KeyGenerator.GetKey().ToString();
        message.Sender = ClientId;
        message.CreateTime = DateTime.UtcNow;
        message.Receiver ??= _clientSetting.Receiver;
    }
    #endregion
}